package com.docmgmt.mvp.security;

import com.docmgmt.mvp.util.JwtTokenProvider;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;

import com.docmgmt.mvp.mapper.RoleMapper;
import com.docmgmt.mvp.mapper.PermissionMapper;
import com.docmgmt.mvp.entity.RoleEntity;
import com.docmgmt.mvp.entity.PermissionEntity;

import org.springframework.security.core.authority.SimpleGrantedAuthority;
import java.util.HashSet;
import java.util.List;
import java.util.Set;


@Slf4j
@Component
@RequiredArgsConstructor
public class JwtAuthenticationFilter extends OncePerRequestFilter {

    private final JwtTokenProvider jwtTokenProvider;
    private final RoleMapper roleMapper;
    private final PermissionMapper permissionMapper;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        try {
            String jwt = getJwtFromRequest(request);

            if (StringUtils.hasText(jwt) && jwtTokenProvider.validateToken(jwt)) {
                Long userId = jwtTokenProvider.getUserIdFromToken(jwt);
                String username = jwtTokenProvider.getUsernameFromToken(jwt);

                // Load authorities from DB (roles + direct user permissions)
                Set<String> codes = new HashSet<>();
                try {
                    // direct permissions
                    List<PermissionEntity> directPerms = permissionMapper.selectDirectPermissionsByUserId(userId);
                    if (directPerms != null) {
                        for (PermissionEntity p : directPerms) {
                            if (p != null && p.getPermissionCode() != null) {
                                codes.add(p.getPermissionCode());
                            }
                        }
                    }
                    // role-based permissions
                    List<RoleEntity> roles = roleMapper.selectRolesByUserId(userId);
                    if (roles != null) {
                        for (RoleEntity r : roles) {
                            if (r == null) continue;
                            List<PermissionEntity> rolePerms = permissionMapper.selectPermissionsByRoleId(r.getId());
                            if (rolePerms != null) {
                                for (PermissionEntity p : rolePerms) {
                                    if (p != null && p.getPermissionCode() != null) {
                                        codes.add(p.getPermissionCode());
                                    }
                                }
                            }
                        }
                    }
                } catch (Exception e) {
                    log.warn("Load authorities failed for user {}: {}", username, e.getMessage());
                }
                List<SimpleGrantedAuthority> authorities = codes.stream()
                        .distinct()
                        .map(SimpleGrantedAuthority::new)
                        .toList();


                UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
                        userId, null, authorities);
                authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));

                SecurityContextHolder.getContext().setAuthentication(authentication);
                log.debug("Set Authentication for user: {}", username);
            }
        } catch (Exception ex) {
            log.error("Could not set user authentication in security context", ex);
        }

        filterChain.doFilter(request, response);
    }

    private String getJwtFromRequest(HttpServletRequest request) {
        String bearerToken = request.getHeader("Authorization");
        if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
            return bearerToken.substring(7);
        }
        return null;
    }
}
